package com.hk.core.autoconfigure.exception.servlet;

import com.hk.commons.util.*;
import com.hk.core.authentication.api.SecurityContextUtils;
import com.hk.core.autoconfigure.exception.ExceptionDetails;
import com.hk.core.autoconfigure.exception.UncaughtExceptionConsumer;
import com.hk.core.web.Webs;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * @author kevin
 * @date 2018-09-10 14:14
 */
public abstract class AbstractExceptionHandler {

    private static final Lazy<UncaughtExceptionConsumer> exceptionConsumerLazy =
            Lazy.of(() -> SpringContextHolder.getBean(UncaughtExceptionConsumer.class));

    static void error(Throwable t, String message, HttpServletRequest request) {
        exceptionConsumerLazy.get().accept(createExceptionDetails(t, message, request)
                .setAsync(Boolean.FALSE), t);
    }

    public static ExceptionDetails createExceptionDetails(Throwable t, String message, HttpServletRequest request) {
        var exceptionDetails = new ExceptionDetails()
                .setId(SnowflakeUtils.getIdGenerator().generate())
                .setRequestTime(LocalDateTime.now())
                .setApplicationId(SpringContextHolder.getApplicationId())
                .setExceptionClassName(t.getClass().getName())
                .setMessage(StringUtils.defaultIfEmpty(message, t.getMessage()));
        try {
            if (Objects.nonNull(request)) {
                exceptionDetails.setIp(Webs.getRemoteAddr(request))
                        .setMethod(request.getMethod())
                        .setUri(request.getServletPath())
                        .setParameters(Webs.getRequestParam(request));
                exceptionDetails.setBody(StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8));
            }
            if (SecurityContextUtils.isAuthenticated()) {
                var principal = SecurityContextUtils.getPrincipal();
                exceptionDetails.setUserId(principal.getUserId())
                        .setAccount(principal.getAccount());
            }
        } catch (IOException e) {
            // ignore
        }
        return exceptionDetails;
    }
}
